<!--
Date: 2025-08-13
Copyright (c) 2025 CAX Conversion Project
-->
<template>
  <div ref="operatorPack" style="width: 100%; height: 100%"></div>
</template>
<script>
export default {
  name: 'CirclePacking'
};
</script>
<script name="CirclePacking" setup>
import * as echarts from 'echarts';
import * as d3 from 'd3';

const props = defineProps(['operatorPackData']);
const operatorPack = ref(null);
const seriesData = ref([]);
const maxDepth = ref(0);
let myChart = null;
const displayRoot = ref(null);

const convert = (source, basePath, depth) => {
  if (source == null) {
    return;
  }
  if (maxDepth.value > 5) {
    return;
  }
  maxDepth.value = Math.max(depth, maxDepth.value);
  let count = 0;
  source.map((el) => {
    count += el.count;
  });
  seriesData.value.push({
    id: basePath,
    value: count,
    depth: depth,
    index: seriesData.value.length
  });
  if (source.length > 0) {
    source.map((el) => {
      if (el.children) {
        var path = basePath + '.' + el.name;
        convert(el.children, path, depth + 1);
      } else {
        seriesData.value.push({
          id: basePath + '.' + el.name,
          value: el.count,
          depth: depth,
          index: seriesData.value.length
        });
      }
    });
  }
};
const prepareData = (rawData) => {
  convert(rawData, '算子', 0);
};
const stratify = () => {
  return d3
    .stratify()
    .parentId(function (d) {
      return d.id.substring(0, d.id.lastIndexOf('.'));
    })(seriesData.value)
    .sum(function (d) {
      return d.value || 0;
    })
    .sort(function (a, b) {
      return b.value - a.value;
    });
};
const overallLayout = (params, api) => {
  var context = params.context;
  d3
    .pack()
    .size([api.getWidth() - 2, api.getHeight() - 2])
    .padding(3)(displayRoot.value);
  context.nodes = {};
  displayRoot.value.descendants().forEach(function (node, index) {
    context.nodes[node.id] = node;
  });
};
const renderItem = (params, api) => {
  var context = params.context;
  if (!context.layout) {
    context.layout = true;
    overallLayout(params, api);
  }
  var nodePath = api.value('id');
  var node = context.nodes[nodePath];
  if (!node) {
    return;
  }
  var isLeaf = !node.children || !node.children.length;
  var focus = new Uint32Array(
    node.descendants().map(function (node) {
      return node.data.index;
    })
  );
  var nodeName = isLeaf
    ? nodePath
        .slice(nodePath.lastIndexOf('.') + 1)
        .split(/(?=[A-Z][^A-Z])/g)
        .join('\n')
    : '';
  var z2 = api.value('depth') * 2;
  return {
    type: 'circle',
    focus: focus,
    shape: {
      cx: node.x,
      cy: node.y,
      r: node.r
    },
    transition: ['shape'],
    z2: z2,
    textContent: {
      type: 'text',
      style: {
        text: nodeName,
        fontFamily: 'Arial',
        width: node.r * 1.3,
        overflow: 'truncate',
        fontSize: node.r / 3
      },
      emphasis: {
        style: {
          overflow: null,
          fontSize: Math.max(node.r / 3, 12)
        }
      }
    },
    textConfig: {
      position: 'inside'
    },
    style: {
      fill: api.visual('color')
    },
    emphasis: {
      style: {
        fontFamily: 'Arial',
        fontSize: 12,
        shadowBlur: 20,
        shadowOffsetX: 3,
        shadowOffsetY: 5,
        shadowColor: 'rgba(0,0,0,0.7)'
      }
    }
  };
};
const drillDown = (targetNodeId) => {
  displayRoot.value = stratify();
  if (targetNodeId != null) {
    displayRoot.value = displayRoot.value.descendants().find(function (node) {
      return node.data.id === targetNodeId;
    });
  }
  displayRoot.value.parent = null;
  myChart.setOption({
    dataset: {
      source: seriesData.value
    }
  });
};
const initChart = () => {
  displayRoot.value = stratify();
  const option = {
    dataset: {
      source: seriesData.value
    },
    tooltip: {},
    visualMap: [
      {
        show: false,
        min: 0,
        max: maxDepth.value,
        dimension: 'depth',
        inRange: {
          color: ['#006edd', '#e0ffff']
        }
      }
    ],
    hoverLayerThreshold: Infinity,
    series: {
      type: 'custom',
      renderItem: renderItem,
      progressive: 0,
      coordinateSystem: 'none',
      encode: {
        tooltip: 'value',
        itemName: 'id'
      }
    }
  };
  myChart.setOption(option);
  myChart.on('click', { seriesIndex: 0 }, function (params) {
    drillDown(params.data.id);
  });
  // Reset: click on the blank area.
  myChart.getZr().on('click', function (event) {
    if (!event.target) {
      drillDown();
    }
  });
};
onMounted(() => {
  myChart = echarts.init(operatorPack.value);
  prepareData(props.operatorPackData);

  initChart();
});
</script>
<style scoped>
</style>